package com.sk.util;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.*;

/**
 * Lambda扩展工具。
 *
 * @author smy
 * {@code @date} 2022/11/5
 */
public class LambdaUtil {

    public static <T> Consumer<T> enumerate(BiConsumer<T, Integer> consumer) {
        AtomicInteger i = new AtomicInteger();
        return t -> consumer.accept(t, i.getAndIncrement());
    }

    public static void nothing(Object o) {

    }

    public static void nothing(Object f, Object s) {

    }

    public static <F, S> void compare(Collection<F> fList, Collection<S> sList, BiPredicate<F, S> comparator, BiConsumer<F, S> eqConsumer) {
        compare(fList, sList, comparator, LambdaUtil::nothing, LambdaUtil::nothing, eqConsumer);
    }

    public static <F, S> void compare(Collection<F> fList, Collection<S> sList, BiPredicate<F, S> comparator, Consumer<F> fConsumer, Consumer<S> sConsumer) {
        compare(fList, sList, comparator, fConsumer, sConsumer, LambdaUtil::nothing);
    }

    public static <F, S> void compare(Collection<F> fList, Collection<S> sList, BiPredicate<F, S> comparator, Consumer<F> fConsumer, Consumer<S> sConsumer, BiConsumer<F, S> eqConsumer) {
        if (fList == null) {
            fList = Collections.emptyList();
        }
        if (sList == null) {
            sList = Collections.emptyList();
        }
        Set<S> hasSSet = new HashSet<>();
        for (F f : fList) {
            boolean flag = false;
            for (S s : sList) {
                if (comparator.test(f, s)) {
                    flag = true;
                    hasSSet.add(s);
                    eqConsumer.accept(f, s);
                }
            }
            if (!flag) {
                fConsumer.accept(f);
            }
        }
        for (S s : sList) {
            if (!hasSSet.contains(s)) {
                sConsumer.accept(s);
            }
        }


    }


    public static <K, V> Map<K, List<V>> listGroup(List<V> list, Function<V, K> kf) {
        return listGroup(list, kf, v -> v);
    }

    public static <D, K, V> Map<K, List<V>> listGroup(List<D> list, Function<D, K> kf, Function<D, V> vf) {
        return listGroup(list, kf, d -> new ArrayList<>(), (d, l) -> {
            V v = vf.apply(d);
            l.add(v);
        });
    }

    public static <D, K, V> Map<K, V> listGroup(List<D> list, Function<D, K> k, Function<D, V> newV, BiConsumer<D, V> hasV) {
        Map<K, V> map = new HashMap<>(list.size());
        list.forEach(o -> {
            K kk = k.apply(o);
            V v = map.get(kk);
            if (v == null) {
                v = newV.apply(o);
                map.put(kk, v);
            }
            hasV.accept(o, v);
        });
        return map;
    }


    /**
     * list 转 map
     *
     * @param list list
     * @param f    k转换方法
     * @param <K>  key类型
     * @param <V>  value类型
     * @return map
     */
    public static <K, V> Map<K, V> list2Map(List<V> list, Function<V, K> f) {
        return list2Map(list, f, (v) -> v);
    }

    /**
     * list 转 map
     *
     * @param list list
     * @param k    k转换方法
     * @param v    v转换方法
     * @param <D>  v类型
     * @param <K>  k类型
     * @param <V>  v类型
     * @return map
     */
    public static <D, K, V> Map<K, V> list2Map(List<D> list, Function<D, K> k, Function<D, V> v) {
        Map<K, V> map = new HashMap<>(list.size());
        list.forEach(o -> map.put(k.apply(o), v.apply(o)));
        return map;
    }

    //对指定类型的数据进行处理，支持Collection、属性
    @SuppressWarnings("unchecked")
    public static <T> void deepConsume(Object object, Class<T> type, Consumer<T> consumer) {
        if (object == null) {
            return;
        }
        if (type.isAssignableFrom(object.getClass())) {
            consumer.accept((T) object);
            return;
        }
        if (object instanceof Collection) {
            ((Collection<?>) object).forEach(o -> deepConsume(o, type, consumer));
            return;
        }
        ObjectUtil.getFields(object.getClass()).stream()
                .filter(f -> type.isAssignableFrom(f.getType()) || List.class.isAssignableFrom(f.getType()))
                .map(field -> {
                    field.setAccessible(true);
                    return ObjectUtil.getValue(object, field);
                })
                .forEach(o -> deepConsume(o, type, consumer));
    }

    //对指定属性数据进行处理，支持Collection、Map、属性
    public static <T> void deepConsume(Object object, Consumer<T> c, String... keys) {
        deep(object, keys, 0, Objects::isNull, c);
    }


    @SuppressWarnings("unchecked")
    public static <T> List<T> deepList(Object object, String... keys) {
        List<T> list = new ArrayList<>();
        deep(object, keys, 0, Objects::isNull, o -> list.add((T) o));
        return list;
    }

    public static <T> T deepMax(Object object, Comparator<? super T> comparator, String... keys) {
        List<T> list = deepList(object, keys);
        return list.stream().max(comparator).orElse(null);
    }

    public static <T> T deepMin(Object object, Comparator<? super T> comparator, String... keys) {
        List<T> list = deepList(object, keys);
        return list.stream().min(comparator).orElse(null);
    }

    @SuppressWarnings("unchecked")
    public static <T> T deepFirst(Object object, String... keys) {
        AtomicReference<T> r = new AtomicReference<>();
        deep(object, keys, 0, o -> r.get() != null || o == null, o -> r.set((T) o));
        return r.get();
    }

    @SuppressWarnings("unchecked")
    private static <T> void deep(Object object, String[] keys, int point, Predicate<Object> p, Consumer<T> consumer) {
        if (p.test(object)) {
            return;
        }
        if (object instanceof Collection) {
            for (Object o : (Collection<?>) object) {
                deep(o, keys, point, p, consumer);
            }
            return;
        }
        if (point < keys.length) {
            if (object instanceof Map) {
                Map<String, ?> map = (Map<String, ?>) object;
                deep(map.get(keys[point]), keys, point + 1, p, consumer);
            } else {
                deep(ObjectUtil.getValue(object, keys[point]), keys, point + 1, p, consumer);
            }
        } else {
            consumer.accept((T) object);
        }
    }

}
